/*
 *  Project:            timelyRV_v1.x -- a RISCV-32IMC SoC.
 *  Module name:        Peri_Top.
 *  Description:        This module is used to connect PE with Periperals.
 *  Last updated date:  2022.04.01.
 *
 *  Communicate with Junnan Li <lijunnan@nudt.edu.cn>.
 *  Copyright (C) 2021-2022 NUDT.
 *
 *  Space = 2;
 */

module Peri_Top(
  //======================= clock & resets  ======================//
   input  wire                                  i_pe_clk
  ,input  wire                                  i_rst_n
  ,input  wire                                  i_spi_clk
  ,input  wire                                  i_sys_clk
  ,input  wire                                  i_sys_rst_n
  //======================= Periperals      ======================//
  //* UART
  ,input  wire    [               `NUM_PE-1:0]  i_uart_rx
  ,output wire    [               `NUM_PE-1:0]  o_uart_tx
  ,input  wire    [               `NUM_PE-1:0]  i_uart_cts
  // //* CAN
  // //,inout  wire    [               `NUM_PE-1:0]  i_can_ad
  //  ,input	wire	[`NUM_PE*8-1:0]               i_can_ad
  //  ,output    wire        [`NUM_PE*8-1:0]               o_can_ad
  //  ,output    wire        [`NUM_PE-1:0]               o_can_ad_sel
  // ,output wire    [               `NUM_PE-1:0]  o_can_cs_n
  // ,output wire    [               `NUM_PE-1:0]  o_can_ale_as
  // ,output wire    [               `NUM_PE-1:0]  o_can_wr_n
  // ,output wire    [               `NUM_PE-1:0]  o_can_rd_n
  // ,output wire    [               `NUM_PE-1:0]  o_can_mode
  // ,input  wire    [               `NUM_PE-1:0]  i_can_int_n
  //* CSR, CSRAM
  //* DMA, dDMA
  ,output wire  [              `NUM_PE*32-1:0]  o_addr_2peri 
  ,output wire  [   `NUM_PE*`NUM_PERI_OUT-1:0]  o_wren_2peri 
  ,output wire  [   `NUM_PE*`NUM_PERI_OUT-1:0]  o_rden_2peri 
  ,output wire  [              `NUM_PE*32-1:0]  o_wdata_2peri
  ,output wire  [               `NUM_PE*4-1:0]  o_wstrb_2peri
  ,input  wire  [`NUM_PE*`NUM_PERI_OUT*32-1:0]  i_rdata_2PBUS
  ,input  wire  [   `NUM_PE*`NUM_PERI_OUT-1:0]  i_ready_2PBUS
  ,input  wire  [   `NUM_PE*`NUM_PERI_OUT-1:0]  i_int_2PBUS  

  //* Peri interface (for 2 PEs)
  ,input  wire  [               `NUM_PE_T-1:0]  i_peri_rden 
  ,input  wire  [               `NUM_PE_T-1:0]  i_peri_wren 
  ,input  wire  [            `NUM_PE_T*32-1:0]  i_peri_addr 
  ,input  wire  [            `NUM_PE_T*32-1:0]  i_peri_wdata
  ,input  wire  [             `NUM_PE_T*4-1:0]  i_peri_wstrb
  ,output wire  [            `NUM_PE_T*32-1:0]  o_peri_rdata
  ,output wire  [               `NUM_PE_T-1:0]  o_peri_ready

  //* irq interface (for 2 PEs)
  ,output wire  [              `NUM_PE*32-1:0]  o_irq    
  ,input  wire  [                 `NUM_PE-1:0]  i_irq_ack
  ,input  wire  [               `NUM_PE*5-1:0]  i_irq_id 

  //* instr/data offset;
  ,output wire  [63:0]                          o_instr_offset_addr
  ,output wire  [63:0]                          o_data_offset_addr
  //* start_en;
  ,input  wire  [3:0]                           i_start_en_pad
  ,output wire  [3:0]                           o_start_en
  ,input  wire  [3:0]                           i_conf_en
  ,output wire                                  o_rst_n_AiPE
  //* system time;
  ,input  wire                                  i_update_valid
  ,input  wire  [64:0]                          i_update_system_time
  ,output wire  [63:0]                          o_system_time
  ,output wire                                  o_second_pulse
  //* debug;   
  //* ready * irq;
  ,output wire  [3:0]                           d_peri_ready_4b
  ,output wire  [8:0]                           d_pe0_int_9b
  ,output wire  [8:0]                           d_pe1_int_9b
  ,output wire  [8:0]                           d_pe2_int_9b
);

  //======================= internal reg/wire/param declarations =//
  //* wire, used to connect SPI, UART, GPIO, CSR, CSRAM, DMA, dDMA, DRA;
  wire  [31:0]                  w_addr_peri[`NUM_PE-1:0];
  wire  [`NUM_PERI-1:0]         w_wren_peri[`NUM_PE-1:0];
  wire  [`NUM_PERI-1:0]         w_rden_peri[`NUM_PE-1:0];
  wire  [31:0]                  w_wdata_peri[`NUM_PE-1:0];
  wire  [3:0]                   w_wstrb_peri[`NUM_PE-1:0];
  wire  [`NUM_PERI*32-1:0]      w_rdata_peri[`NUM_PE-1:0];
  wire  [`NUM_PERI-1:0]         w_ready_peri[`NUM_PE-1:0];
  wire  [`NUM_PERI:0]           w_int_peri[`NUM_PE-1:0];

  wire  [31:0]                  w_addr_peri_in[`NUM_PE-1:0];
  wire  [`NUM_PERI_IN-1:0]      w_wren_peri_in[`NUM_PE-1:0];
  wire  [`NUM_PERI_IN-1:0]      w_rden_peri_in[`NUM_PE-1:0];
  wire  [31:0]                  w_wdata_peri_in[`NUM_PE-1:0];
  wire  [`NUM_PERI_IN*4-1:0]    w_wstrb_peri_in[`NUM_PE-1:0];
  wire  [`NUM_PERI_IN*32-1:0]   w_rdata_peri_in[`NUM_PE-1:0];
  wire  [`NUM_PERI_IN-1:0]      w_ready_peri_in[`NUM_PE-1:0];
  wire  [`NUM_PERI_IN-1:0]      w_int_peri_in[`NUM_PE-1:0];

  //* time interrupt;
  wire  [`NUM_PE-1:0]           w_time_int;
  //==============================================================//

  //======================= Combine Peri_out/in ==================//
  genvar i_pe;
  generate
    for (i_pe = 0; i_pe < `NUM_PE; i_pe=i_pe+1) begin : Peri_PE
      assign o_addr_2peri[i_pe*32+:32]    = w_addr_peri[i_pe];
      assign w_addr_peri_in[i_pe]         = w_addr_peri[i_pe];
      assign {o_wren_2peri[i_pe*`NUM_PERI_OUT+:`NUM_PERI_OUT],        w_wren_peri_in[i_pe]}   = w_wren_peri[i_pe];
      assign {o_rden_2peri[i_pe*`NUM_PERI_OUT+:`NUM_PERI_OUT],        w_rden_peri_in[i_pe]}   = w_rden_peri[i_pe];
      assign o_wdata_2peri[i_pe*32+:32]   = w_wdata_peri[i_pe];
      assign w_wdata_peri_in[i_pe]        = w_wdata_peri[i_pe];
      assign o_wstrb_2peri[i_pe*4+:4]     = w_wstrb_peri[i_pe];
      assign w_wstrb_peri_in[i_pe]        = w_wstrb_peri[i_pe];

      assign w_rdata_peri[i_pe] = {i_rdata_2PBUS[i_pe*`NUM_PERI_OUT*32+:`NUM_PERI_OUT*32],  w_rdata_peri_in[i_pe]};
      assign w_ready_peri[i_pe] = {i_ready_2PBUS[i_pe*`NUM_PERI_OUT+:`NUM_PERI_OUT],        w_ready_peri_in[i_pe]};
      assign w_int_peri[i_pe]   = {w_time_int[i_pe], i_int_2PBUS[i_pe*`NUM_PERI_OUT+:`NUM_PERI_OUT], w_int_peri_in[i_pe]};
    end
  endgenerate
  //==============================================================//

  //======================= Periperal_Bus ========================//
  generate
    for (i_pe = 0; i_pe < `NUM_PE; i_pe = i_pe+1) begin: peri_bus
      Periperal_Bus Periperal_Bus (
        //* clk & rst_n;
        .i_clk              (i_pe_clk                     ),
        .i_rst_n            (i_rst_n                      ),
        //* peri interface;
        .i_peri_rden        (i_peri_rden[i_pe]            ),
        .i_peri_wren        (i_peri_wren[i_pe]            ),
        .i_peri_addr        (i_peri_addr[i_pe*32+:32]     ),
        .i_peri_wdata       (i_peri_wdata[i_pe*32+:32]    ),
        .i_peri_wstrb       (i_peri_wstrb[i_pe*4+:4]      ),
        .o_peri_rdata       (o_peri_rdata[i_pe*32+:32]    ),
        .o_peri_ready       (o_peri_ready[i_pe]           ),
        .o_peri_gnt         (                             ),
        //* conncet DMA, dDMA, CSRAM, CSR, SPI, GPIO, UART;
        .o_addr_2peri       (w_addr_peri[i_pe]            ),
        .o_wren_2peri       (w_wren_peri[i_pe]            ),
        .o_rden_2peri       (w_rden_peri[i_pe]            ),
        .o_wdata_2peri      (w_wdata_peri[i_pe]           ),
        .o_wstrb_2peri      (w_wstrb_peri[i_pe]           ),
        .i_rdata_2PBUS      (w_rdata_peri[i_pe]           ),
        .i_ready_2PBUS      (w_ready_peri[i_pe]           )
      );
    end
  endgenerate
  //==============================================================//

  //======================= Interrupt_Ctrl & Uart ================//
  generate
    for (i_pe = 0; i_pe < `NUM_PE; i_pe = i_pe+1) begin: peri_int
      //* INT_CTRL;
      Interrupt_Ctrl Interrupt_Ctrl(
        .i_clk              (i_pe_clk                     ),
        .i_rst_n            (i_rst_n                      ),
        .i_irq              (w_int_peri[i_pe]             ),
        .o_irq              (o_irq[i_pe*32+:32]           ),
        .i_irq_ack          (i_irq_ack[i_pe]              ),
        .i_irq_id           (i_irq_id[i_pe*5+:5]          )
      );
    end
  endgenerate

  generate
    for (i_pe = 0; i_pe < `NUM_PE; i_pe = i_pe+1) begin: peri_uart
      //* UART;
      UART_TOP UART_TOP(
        //* clk & rst_n;
        .i_clk              (i_pe_clk                           ),
        .i_rst_n            (i_rst_n                            ),
        .i_sys_clk          (i_sys_clk                          ),
        .i_sys_rst_n        (i_sys_rst_n                        ),
        //* uart recv/trans;
        .o_uart_tx          (o_uart_tx[i_pe]                    ),
        .i_uart_rx          (i_uart_rx[i_pe]                    ),
        .i_uart_cts         (i_uart_cts[i_pe]                   ),
        //* peri interface;
        .i_addr_32b         (w_addr_peri_in[i_pe]               ),
        .i_wren             (w_wren_peri_in[i_pe][`UART]        ),
        .i_rden             (w_rden_peri_in[i_pe][`UART]        ),
        .i_din_32b          (w_wdata_peri_in[i_pe]              ),
        .o_dout_32b         (w_rdata_peri_in[i_pe][`UART*32+:32]),
        .o_dout_32b_valid   (w_ready_peri_in[i_pe][`UART]       ),
        .o_interrupt        (w_int_peri_in[i_pe][`UART]         )
      );
    end
  endgenerate
  //==============================================================//

  // //======================= CAN ==================================//
  // generate
  //   for (i_pe = 0; i_pe < `NUM_PE; i_pe = i_pe+1) begin: peri_can
  //     //* UART;
  //     CAN_Top CAN_TOP(
  //       //* clk & rst_n;
  //       .i_can_clk          (i_sys_clk                          ),
  //       .i_can_rst_n        (i_sys_rst_n                        ),
  //       .i_pe_clk           (i_pe_clk                           ),
  //       .i_pe_rst_n         (i_rst_n                            ),
  //       //* peri interface;
  //       .i_addr_32b         (w_addr_peri_in[i_pe]               ),
  //       .i_wren             (w_wren_peri_in[i_pe][`CAN]         ),
  //       .i_rden             (w_rden_peri_in[i_pe][`CAN]         ),
  //       .i_din_32b          (w_wdata_peri_in[i_pe]              ),
  //       .o_dout_32b         (w_rdata_peri_in[i_pe][`CAN*32+:32] ),
  //       .o_dout_32b_valid   (w_ready_peri_in[i_pe][`CAN]        ),
  //       .o_interrupt        (w_int_peri_in[i_pe][`CAN]          ),
  //       //* can SJA_WR;
  //      // .can_ad             (i_can_ad[i_pe]                     ),
  //      .can_ad_i           (i_can_ad[7+i_pe*8:0+i_pe*8]        ),
  //     .can_ad_o           (o_can_ad[7+i_pe*8:0+i_pe*8]        ),
  //     .can_ad_sel         (o_can_ad_sel[i_pe]                 ),
  //       .can_cs_n           (o_can_cs_n[i_pe]                   ),
  //       .can_ale_as         (o_can_ale_as[i_pe]                 ),
  //       .can_wr_n           (o_can_wr_n[i_pe]                   ),
  //       .can_rd_n           (o_can_rd_n[i_pe]                   ),
  //       .can_mode           (o_can_mode[i_pe]                   ),
  //       .can_int_n          (i_can_int_n[i_pe]                  )
  //     );
  //   end
  // endgenerate
  // //==============================================================//
  generate
    for (i_pe = 0; i_pe < `NUM_PE; i_pe = i_pe+1) begin: peri_can
        assign w_ready_peri_in[i_pe][`CAN]  = 1'b0;
        assign w_int_peri_in[i_pe][`CAN]    = 1'b0;
    end
  endgenerate


  //======================= Control Status Registers =============//
  wire  [`NUM_PE*32-1:0]    w_addr_peri_in_CSR;
  wire  [`NUM_PE-1:0]       w_wren_peri_in_CSR;
  wire  [`NUM_PE-1:0]       w_rden_peri_in_CSR;
  wire  [`NUM_PE*32-1:0]    w_wdata_peri_in_CSR;
  wire  [`NUM_PE*32-1:0]    w_rdata_peri_in_CSR;
  wire  [`NUM_PE-1:0]       w_ready_peri_in_CSR;
  wire  [`NUM_PE-1:0]       w_int_peri_in_CSR;
  generate
    for (i_pe = 0; i_pe < `NUM_PE; i_pe = i_pe+1) begin: peri_csr
      assign w_addr_peri_in_CSR[i_pe*32+:32]    = w_addr_peri_in[i_pe];
      assign w_wren_peri_in_CSR[i_pe]           = w_wren_peri_in[i_pe][`CSR];
      assign w_rden_peri_in_CSR[i_pe]           = w_rden_peri_in[i_pe][`CSR];
      assign w_wdata_peri_in_CSR[i_pe*32+:32]   = w_wdata_peri_in[i_pe];
      assign w_rdata_peri_in[i_pe][`CSR*32+:32] = w_rdata_peri_in_CSR[i_pe*32+:32];
      assign w_ready_peri_in[i_pe][`CSR]        = w_ready_peri_in_CSR[i_pe];
      assign w_int_peri_in[i_pe][`CSR]          = w_int_peri_in_CSR[i_pe];
    end
  endgenerate

  //* CSR module
  CSR_TOP CSR_TOP(
    //* clk & rst_n;
    .i_clk                  (i_pe_clk                           ),
    .i_rst_n                (i_rst_n                            ),
    //* peri interface;
    .i_addr_32b             (w_addr_peri_in_CSR                 ),
    .i_wren                 (w_wren_peri_in_CSR                 ),
    .i_rden                 (w_rden_peri_in_CSR                 ),
    .i_din_32b              (w_wdata_peri_in_CSR                ),
    .o_dout_32b             (w_rdata_peri_in_CSR                ),
    .o_dout_32b_valid       (w_ready_peri_in_CSR                ),
    .o_interrupt            (w_int_peri_in_CSR                  ),
    .o_time_int             (w_time_int                         ),
    //* instr/data offset;
    .o_instr_offset_addr    (o_instr_offset_addr                ),
    .o_data_offset_addr     (o_data_offset_addr                 ),
    //* system time;
    .i_update_valid         (i_update_valid                     ),
    .i_update_system_time   (i_update_system_time               ),
    .o_system_time          (o_system_time                      ),
    .o_second_pulse         (o_second_pulse                     ),
    //* start_en;
    .i_start_en_pad         (i_start_en_pad                     ),
    .o_start_en             (o_start_en                         ),
    .i_conf_en              (i_conf_en                          ),
    .o_rst_n_AiPE           (o_rst_n_AiPE                       )
  );
  //==============================================================//
    
  //* debug;
  assign d_peri_ready_4b        = o_peri_ready;
  assign d_pe0_int_9b           = 0;
  assign d_pe1_int_9b           = 0;
  assign d_pe2_int_9b           = 0;

endmodule    
